<?php

class Nuo_Html_Element
{

	/**
	 * @var Nuo_Html_Element_Element
	 */
	protected $_parent = NULL;

	/**
	 * @var bool
	 */
	protected $_isMulti = false;

	/**
	 * @var string
	 */
	protected $_name = '';

	/**
	 * @var mixed
	 */
	protected $_value = NULL;

	/**
	 * other options like html attributes
	 */
	protected $_attrib = array();

	/**
	 * meta data options
	 * @var array
	 */
	protected $_meta = array();

	/**
	 * childrens
	 * @var array
	 */
	protected $_children = array();

	/**
	 * error messages
	 */
	protected $_errors = array();

	/**
	 * @var array
	 * validator listing
	 */
	protected $_rules = array();

	/**
	 * @var string
	 */
	protected $_notice = '';

	/**
	 * set wraper class
	 */
	protected $_wrapperClass = '';

	/**
	 * @var array
	 */
	static protected $_render = array();

	/**
	 * @param  string  $className  valid css class name
	 * @return Nuo_Html_Element_Element
	 */
	function setWrapperClass($className)
	{
		$this -> _wrapperClass = $className;
		return $this;
	}

	/**
	 * get Wrapper Css Class Name
	 * @return string
	 */
	function getWrapperClass()
	{
		return $this -> _wrapperClass;
	}

	/**
	 * @param string $notice
	 * @return Nuo_Html_Element_Element
	 */
	function setNotice($notice)
	{
		$this -> _notice = $notice;
		return $this;
	}

	/**
	 * @return string
	 */
	function getNotice()
	{
		return $this -> _notice;
	}

	public function setParent($ele)
	{
		$this -> _parent = $ele;
		return $this;
	}

	function getParent()
	{
		return $this -> _parent;
	}

	/**
	 * @param string $name
	 * @return Nuo_Html_Element_Element
	 */
	public function getChild($name)
	{
		return isset($this -> _children[$name]) ? $this -> _children[$name] : null;
	}

	/**
	 * @param string $name
	 * @return Nuo_Html_Element_Element
	 */
	public function removeChild($name)
	{
		if (isset($this -> _children[$name]))
		{
			unset($this -> _children[$name]);
		}
		return $this;
	}

	public function setIsMulti($flag)
	{
		$this -> _isMulti = (bool)$flag;
		return $this;
	}

	public function getIsMulti()
	{
		return $this -> _isMulti;
	}

	public function isMulti()
	{
		return $this -> _isMulti;
	}

	/**
	 * @param array
	 * @return Nuo_Html_Element_Element
	 */
	public function setChildren($values)
	{
		foreach ($values as $value)
		{
			$this -> addChild($value['type'], $value['name'], $value['options']);
		}
		return $this;
	}

	/**
	 * get array of element
	 * @return array
	 */
	public function getChildren()
	{
		return $this -> _children;
	}

	/**
	 * check element exists
	 * @return bool
	 */
	public function hasChild($name)
	{
		return isset($this -> _children[$name]);
	}

	/**
	 * @return bool
	 */
	public function hasChildren()
	{
		return empty($this -> _children);
	}

	/**
	 * @param string $name
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setMeta($name, $value = null)
	{
		if (!is_string($name) && is_array($name))
		{
			foreach ($name as $k => $v)
			{
				$this -> _meta[$k] = $v;
			}
		}
		else
		if ($name)
		{
			$this -> _meta[$name] = $value;
		}
		return $this;
	}

	/**
	 * @param string $name
	 * @param string $default
	 * @return string
	 */
	public function getMeta($name = null, $default = null)
	{
		if (null == $name)
		{
			return $this -> _meta;
		}
		return isset($this -> _meta[$name]) ? $this -> _meta[$name] : $default;
	}

	/**
	 * clear all meta options
	 * @return Nuo_Html_Element_Elment
	 */
	public function clearMeta()
	{
		$this -> _meta = array();
		return $this;
	}

	/**
	 * remove meta config
	 */
	public function removeMeta($name)
	{
		if (isset($this -> _meta[$name]))
		{
			unset($this -> _meta[$name]);
		}
		return $this;
	}

	/**
	 * @param string $name
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setAttrib($name, $value = null)
	{
		if (!is_string($name) && is_array($name))
		{
			foreach ($name as $k => $v)
			{
				$this -> _attrib[$k] = $v;
			}
		}
		else
		if ($name)
		{
			$this -> _attrib[$name] = $value;
		}
		return $this;
	}

	/**
	 * @return array
	 */
	public function getAttribs()
	{
		$attr = $this -> _attrib;
		$attr['name'] = $this -> _name;
		return $attr;
	}

	/**
	 * @param string $name
	 * @param string $value
	 * @return string
	 */
	public function getAttrib($name = null, $default = null)
	{
		if (null == $name)
		{
			return $this -> _attrib;
		}

		return isset($this -> _attrib[$name]) ? $this -> _attrib[$name] : $default;
	}

	/**
	 * clear all attribute
	 * @return Nuo_Html_Element_Element
	 */
	public function clearAttrib()
	{
		$this -> _attrib = array();
		return $this;
	}

	/**
	 * remove attribute
	 * @return Nuo_Html_Element_Element
	 */
	public function removeAttrib($name)
	{
		if (isset($this -> _attrib[$name]))
		{
			unset($this -> _attrib[$name]);
		}
		return $this;
	}

	/**
	 * base method, need to override from super class
	 */
	public function init()
	{

	}

	/**
	 * @param array $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setErrors($value)
	{
		$this -> _errors = $value;
		return $this;
	}

	/**
	 * @return array
	 */
	public function getErrors()
	{
		return $this -> _errors;
	}

	/**
	 * clear all errors
	 * @return Nuo_Html_Element_Element
	 */
	public function clearError()
	{
		$this -> _errors = array();
		return $this;
	}

	/**
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function addError($msg)
	{
		$this -> _errors[] = $msg;
		return $this;
	}

	public function setName($name)
	{
		if ($this -> isMulti())
		{
			$name .= '[]';
		}
		$this -> _name = $name;
		return $this;
	}

	public function getName()
	{
		return $this -> _name;
	}

	public function setValue($value)
	{
		$this -> _value = $value;
		return $this;
	}

	public function getValue()
	{
		return $this -> _value;
	}

	public function clearValue()
	{
		$this -> _value = NULL;
		return $this;
	}

	public function setOptions($options)
	{
		foreach ($options as $key => $value)
		{
			if (method_exists($this, $method = 'set' . ucfirst($key)))
			{
				$this -> {$method}($value);
			}
			else
			{
				$this -> _attrib[$key] = $value;
			}
		}
		return $this;
	}

	/**
	 * render method
	 */
	public function render()
	{
		$this -> beforeRender();

		$class = 'Nuo_Html_Render_' . ucfirst($this -> getRender());

		if (!isset(self::$_render[$class]))
		{
			self::$_render[$class] = new $class;
		}

		return self::$_render[$class] -> render($this);
	}

	public function __toString()
	{
		return $this -> render();
	}

	/**
	 * constructor
	 * @return void
	 */
	public function __construct()
	{
		$this -> init();
	}

	public function beforeRender()
	{

	}

	public function __flat()
	{
		$xhtml = array();

		$attribs = $this -> getAttribs();

		$attribs['name'] = $this -> _name;
		$attribs['id'] = $this -> _name;

		foreach ($attribs as $key => $value)
		{
			$xhtml[] = $key . '="' . $value . '"';
		}
		return implode(' ', $xhtml);
	}

	/**
	 * alias call
	 * @return string
	 */
	public function getDesc()
	{
		return isset($this -> _meta['desc']) ? $this -> _meta['desc'] : '';
	}

	/**
	 * alias call
	 * @return string
	 */
	public function getLabel()
	{
		return isset($this -> _meta['label']) ? $this -> _meta['label'] : '';
	}

	/**
	 * alias call
	 * @return string
	 */
	public function getTitle()
	{
		return isset($this -> _meta['title']) ? $this -> _meta['title'] : '';
	}

	/**
	 * alias call
	 * @return string
	 */
	public function getNoLabel()
	{
		return isset($this -> _meta['noLabel']) ? $this -> _meta['noLabel'] : 0;
	}

	/**
	 * alias call
	 * @return string
	 */
	public function getNoTitle()
	{
		return isset($this -> _meta['noTitle']) ? $this -> _meta['noTitle'] : 0;
	}

	/**
	 * alias call
	 * @return string
	 */
	public function getNoDesc()
	{
		return isset($this -> _meta['noDesc']) ? $this -> _meta['noDesc'] : 0;
	}

	/**
	 * alias call
	 * @return string
	 */
	public function getRender()
	{
		return isset($this -> _meta['render']) ? $this -> _meta['render'] : 'input';
	}

	/**
	 * @return string
	 */
	public function getTagName()
	{
		return isset($this -> _meta['tagName']) ? $this -> _meta['tagName'] : 'span';
	}

	/**
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setTagName($value)
	{
		$this -> _meta[$name] = $value;
		return $this;
	}

	/**
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setDesc($value)
	{
		$this -> _meta['desc'] = $value;
		return $this;
	}

	/**
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setLabel($value)
	{
		$this -> _meta['label'] = $value;
		return $this;
	}

	/**
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setNoLabel($value)
	{
		$this -> _meta['noLabel'] = $value;
		return $this;
	}

	/**
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setNoTitle($value)
	{
		$this -> _meta['noTitle'] = $value;
		return $this;
	}

	/**
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setNoDesc($value)
	{
		$this -> _meta['noDesc'] = $value;
		return $this;
	}

	/**
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setTag($value)
	{
		$this -> _meta['tag'] = $value;
		return $this;
	}

	/**
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setRender($value)
	{
		$this -> _meta['render'] = $value;
		return $this;
	}

	/**
	 * @param string $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setTitle($value)
	{
		$this -> _meta['title'] = $value;
		return $this;
	}

	/**
	 * @param bool $value
	 * @return Nuo_Html_Element_Element
	 */
	public function setRequired($value)
	{
		$this -> _meta['required'] = (bool)$value;
		return $this;
	}

	/**
	 * @return bool
	 */
	public function getRequired()
	{
		return isset($this -> _meta['required']) ? $this -> _meta['required'] : false;
	}

	/**
	 * @param array $values
	 * @return array
	 */
	public function setRules($rules)
	{
		if (is_string($rules))
		{
			$this -> _rules = explode(',', $rules);
		}
		else
		{
			$this -> _rules = $rules;
		}
		return $this;
	}

	/**
	 * alias call
	 * @return array
	 */
	public function getRules()
	{
		return $this -> _rules;
	}

	/**
	 * @param array $values
	 * @return Nuo_Html_Element_Element
	 */
	public function setFilters($values)
	{
		$this -> _meta['filter'] = $values;
		return $this;
	}

	/**
	 * @return array
	 */
	public function getFilters()
	{
		return isset($this -> _meta['filter']) ? $this -> _meta['filter'] : array();
	}

	/**
	 * magic getter method.
	 * @param string $name
	 * @return null
	 */
	public function __get($name)
	{
		if (isset($this -> _children[$name]))
		{
			return $this -> _children[$name];
		}

		if (isset($this -> _meta[$name]))
		{
			return $this -> _meta[$name];
		}

		if (isset($this -> _attrib[$name]))
		{
			return $this -> _attrib[$name];
		}

		if (method_exists($this, $method = 'get' . ucfirst($name)))
		{
			return $this -> {$method}();
		}

		return NULL;
	}

	/**
	 * check is valid;
	 * @return bool
	 */
	protected function isValid($value)
	{

		$valid = true;

		foreach ($this->_rules as $name => $config)
		{

			if (is_int($name))
			{
				$name = $config;
				$config = null;
			}
			elseif (is_string($name))
			{
				$config = array('msg' => $config);
			}

			$class = 'Nuo_Validator_' . ucfirst($name);

			$obj = new $class($config);

			if (!$obj -> isValid($value))
			{
				$this -> addError($obj -> getMsg());
				$valid = false;
			}
		}

		$this -> setValue($value);

		return $valid;
	}

	public function getData()
	{
		return $this -> getValue();
	}

	public function setData($data)
	{
		return $this -> setValue($data);
	}

}
